`timescale 1ns / 1ns
//本module实现的是全连接网络第三层，输入通道32，输出通道2
//每次并行处理32维，共1组

module fc_3(
    input  wire         clk_i,
    input  wire         rst_n_i,
    input  wire         start_i,
    output wire         done_o,

    input  wire [1535:0] input_data_i,          // 32 * 48-bit

    output reg  [0:0]    weight_addr_o,         // {out_channel[0:0]}
    input  wire [255:0]  weight_i,              // 32 * 8-bit

    output reg  [0:0]    bias_addr_o,
    input  wire [7:0]    bias_i,                // 8-bit signed

    output reg           fc_output_wren_o,
    output reg  [63:0]   fc_output_data_o,
    output reg  [0:0]    fc_output_addr_o,

    output wire [1535:0] mul_data1_o,
    output wire [255:0] mul_data2_o,
    input  wire [1791:0] mul_result_i           // 32 * 56-bit (48x8)
	);
    parameter INPUT_DIM    = 32;
    parameter OUTPUT_DIM   = 2;
    parameter MULT_LATENCY = 5;
    parameter RAM_LATENCY  = 3;

    // 主控信号
    reg       running;
    reg [0:0] launch_ptr;        // 当前发起的输出通道
    reg       launch_done;       // 是否已发起所有输出通道

    // 控制信号延迟链：使用参数化索引，覆盖到 DONE 阶段
    reg [0:0] out_channel_dly [0:16];

    // 偏置同样来自 BRAM (3 拍延迟)，后续再加移位链对齐
    // 估计最大需要的偏置移位级数 = (INDEX_BIAS - RAM_LATENCY)
    reg [7:0]    bias_reg  [0:8];

    // 加法树结果
    wire [63:0] final_sum;

    // ================== 流水线阶段索引参数化 ==================
    // Address 发起为 0
    localparam INDEX_ADDR      = 0;                                  // 地址阶段
    localparam INDEX_RAM_OUT   = INDEX_ADDR + RAM_LATENCY;           // RAM 输出可用于乘法输入 (3)
    localparam INDEX_MUL_OUT   = INDEX_RAM_OUT + MULT_LATENCY;       // 乘法器输出 (3+5=8)
    localparam ADDER_TREE_STAGES = 2;                                // adder_tree 内部寄存级数
    localparam INDEX_ADDER_OUT = INDEX_MUL_OUT + ADDER_TREE_STAGES;  // 加法树输出 (8+2=10)
    localparam INDEX_BIAS      = INDEX_ADDER_OUT + 1;                // 加偏置(11)
    localparam INDEX_WRITE     = INDEX_BIAS + 1;                     // 写回(12)
    localparam INDEX_DONE      = INDEX_WRITE + 2;                    // done 输出(14), 为了延迟1个时钟, +2而不是+1

    // 数据路径寄存
    reg [63:0] accumulator;  // 偏置相加后的结果

    reg [INDEX_DONE:0] valid_shift;

    // 发起控制信号
    wire       launch_enable = running && !launch_done;

    // 启动 / 运行控制
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            running <= 1'b0;
        end else begin
            if(start_i && !running)
                running <= 1'b1;
            else if(done_o) // 流水线完全结束
                running <= 1'b0;
        end
    end

    // 发起通道递增逻辑
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            launch_ptr  <= 0;
            launch_done <= 1'b0;
        end else if(!running) begin
            launch_ptr  <= 0;
            launch_done <= 1'b0;
        end else if(launch_enable) begin
            if(launch_ptr == OUTPUT_DIM-1)
                launch_done <= 1'b1;
            else
                launch_ptr <= launch_ptr + 1'b1;
        end
    end

    // out_channel 延迟链 (简单移位)
    integer d;
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            for(d=0; d<=16; d=d+1)
                out_channel_dly[d] <= 0;
        end else begin
            out_channel_dly[0] <= launch_enable ? launch_ptr : 0;
            for(d=1; d<=16; d=d+1)
                out_channel_dly[d] <= out_channel_dly[d-1];
        end
    end

    // 有效位移位寄存器 (跟踪流水线中有效数据)
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i)
            valid_shift <= 0;
        else
            valid_shift <= {valid_shift[INDEX_DONE-1:0], launch_enable};
    end

    // 偏置延迟链：bias_i 已包含 BRAM 3 拍延迟，这里再移位用于对齐到 INDEX_BIAS
    integer b;
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            for(b=0;b<=8;b=b+1) bias_reg[b] <= 0;
        end else begin
            bias_reg[0] <= bias_i;
            for(b=1;b<=8;b=b+1)
                bias_reg[b] <= bias_reg[b-1];
        end
    end

    // 阶段1：地址生成（权重 / 偏置均来自 BRAM，输入直接并行）
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            weight_addr_o <= 0;
            bias_addr_o   <= 0;
        end else if(launch_enable) begin
            weight_addr_o <= launch_ptr;
            bias_addr_o   <= launch_ptr;
        end
    end

    // 乘法器输入
    assign mul_data1_o = input_data_i;
    assign mul_data2_o = weight_i;

    // 阶段10-11：加法树 (2级流水)
    adder_tree_3 u_adder_tree (
        .clk    (clk_i),
        .rst_n  (rst_n_i),
        .data   (mul_result_i),
        .sum_out(final_sum)
    );

    // 阶段12：加偏置
    /*这里的常数10、6是观察仿真波形“数”出来的，不是计算值*/
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            accumulator <= 0;
        end else if(valid_shift[10]) begin
            accumulator <= final_sum + {{56{bias_reg[6][7]}}, bias_reg[6]};
        end else begin
            accumulator <= 0;
        end
    end

    // 阶段13：写输出
    /*这里的常数11是观察仿真波形“数”出来的，不是计算值*/
    always @(posedge clk_i or negedge rst_n_i) begin
        if(!rst_n_i) begin
            fc_output_wren_o <= 0;
            fc_output_data_o <= 0;
            fc_output_addr_o <= 0;
        end else if(valid_shift[11]) begin
            fc_output_wren_o <= 1'b1;
            fc_output_addr_o <= out_channel_dly[11];
            fc_output_data_o <= accumulator;
        end else begin
            fc_output_wren_o <= 1'b0;
        end
    end

    // done 在流水线末端输出
    /*这里的常数13是观察仿真波形“数”出来的，不是计算值*/
    assign done_o = valid_shift[13] && (out_channel_dly[13] == (OUTPUT_DIM-1));

endmodule
